package cn.wgx.module.mind.service;

import cn.hutool.core.io.FileUtil;
import cn.wgx.common.base.BaseLogger;
import cn.wgx.common.entity.mind.*;
import cn.wgx.common.util.OSInfo;
import cn.wgx.common.util.ReflectionUtils;
import cn.wgx.common.util.StringUtils;
import cn.wgx.common.util.UUIDUtils;
import cn.wgx.module.mind.entity.Srt;
import cn.wgx.module.mind.util.SrtUtil;
import cn.wgx.module.mind.util.aspose.LicenseVerify;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aspose.words.DocumentBuilder;
import com.aspose.words.Font;
import com.aspose.words.FontSettings;
import com.aspose.words.SaveFormat;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Service
public class DirectorService extends BaseLogger {

    @Autowired
    ChannelService channelService;
    @Autowired
    BlockService blockService;
    @Autowired
    GroupService groupService;
    @Autowired
    BeatService beatService;
    @Autowired
    BeatContentService beatContentService;
    @Autowired
    DivideService divideService;
    @Autowired
    LinkService linkService;

    public JSONObject getAll(Integer pro_id, Boolean tree) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("pro_id", pro_id);
        List<MindLink> links = linkService.list(queryWrapper);
        List<MindChannel> channels = channelService.list(queryWrapper);
        queryWrapper.orderByAsc("x");
        List<MindBlock> blocks = blockService.list(queryWrapper);
        List<MindBeat> beats = beatService.list(queryWrapper);
        List<MindGroup> groups = groupService.list(queryWrapper);
        List<MindDivide> divides = divideService.list(queryWrapper);
        if (tree) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("root", tree(channels, blocks, beats, groups));
            jsonObject.put("divides", divides);
            jsonObject.put("links", links);
            return jsonObject;
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("channels", channels);
        jsonObject.put("blocks", blocks);
        jsonObject.put("beats", beats);
        jsonObject.put("groups", groups);
        jsonObject.put("divides", divides);
        jsonObject.put("links", links);
        return jsonObject;
    }


    public JSONArray tree(List<MindChannel> channels, List<MindBlock> blocks, List<MindBeat> beats, List<MindGroup> groups) {

        JSONArray res = new JSONArray();

        for (MindChannel channel : channels) {
            if (channel == null) {
                continue;
            }
            Integer channelId = channel.getChannel_id();
            JSONObject channelJson = (JSONObject) JSONObject.toJSON(channel);
            if (channel.getType().equals("1")) {
                channelJson.put("e_type", "blockChannel");
                JSONArray channelChildren = new JSONArray();
                //块通道添加区块
                for (MindBlock block : blocks) {
                    if (block == null) {
                        continue;
                    }
                    if (block.getChannel_id().equals(channelId)) {
                        JSONObject blockJson = (JSONObject) JSONObject.toJSON(block);
                        blockJson.put("e_type", "block");
                        //添加子节点,包含节点和组
                        blockJson.put("children", addBeatAndGroup(block.getChannel_id(), block.getBlock_id(), beats, groups));
                        channelChildren.add(blockJson);
                    }
                }
                channelJson.put("children", channelChildren);
            } else if (channel.getType().equals("2")) {
                channelJson.put("e_type", "freeChannel");
                channelJson.put("children", addBeatAndGroup(channel.getChannel_id(), 0, beats, groups));
                //排序根据sort字段

            }
//            channelJson.put("children", channelChildren);
            res.add(channelJson);
        }

        return res;
    }

    //添加子情节点和组
    private JSONArray addBeatAndGroup(Integer channelId, Integer blockId, List<MindBeat> beats, List<MindGroup> groups) {
        if (channelId == null || blockId == null || channelId.equals(0)) {
            return new JSONArray();
        }
        JSONArray children = new JSONArray();
        //自由通道添加节点
        for (MindBeat beat : beats) {
            if (beat == null) {
                continue;
            }
            if (beat.getChannel_id().equals(channelId) && beat.getBlock_id().equals(blockId)) {
                JSONObject beatJson = (JSONObject) JSONObject.toJSON(beat);
                beatJson.put("e_type", "beat");
                children.add(beatJson);
            }
        }
        //添加编组
        for (MindGroup group : groups) {
            if (group == null) {
                continue;
            }
            if (group.getChannel_id().equals(channelId) && group.getBlock_id().equals(blockId)) {
                JSONObject groupJson = (JSONObject) JSONObject.toJSON(group);
                groupJson.put("e_type", "group");
                Integer group_id = group.getGroup_id();
                Integer pro_id = group.getPro_id();
                JSONArray groupChildren = new JSONArray();
                for (MindBeat beat : beats) {
                    if (beat == null) {
                        continue;
                    }
                    if (beat.getGroup_id().equals(group_id) && beat.getPro_id().equals(pro_id)) {
                        JSONObject beatJson = (JSONObject) JSONObject.toJSON(beat);
                        beatJson.put("e_type", "beat");
                        groupChildren.add(beatJson);
                    }
                }
                groupJson.put("children", groupChildren);
                children.add(groupJson);
            }
        }
        //排序根据sort字段
        Collections.sort(children, Comparator.comparingInt(json -> ((JSONObject) json).getInteger("sort")));
        return children;
    }

    /**
     * 根据图片列表导出mp4
     *
     * @param pro_id
     * @param response
     * @return
     */
    public Object exportMp4(Integer pro_id, HttpServletResponse response) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("pro_id", pro_id);
        List<MindBeatContent> mindBeatContents = beatContentService.list(queryWrapper);
        queryWrapper.orderByAsc("x");
        List<MindBeat> beats = beatService.list(queryWrapper);
        //根据beats顺序排序beatContents
        for(int i=0,n=beats.size(),m=0,z=mindBeatContents.size();i<n;i++){
            MindBeat mindBeat = beats.get(i);
            Integer beat_id = mindBeat.getBeat_id();
            for(int j=m;j<z;j++){
                MindBeatContent mindBeatContent = mindBeatContents.get(j);
                if(mindBeatContent != null){
                    if(mindBeatContent.getBeat_id().equals(beat_id)){
                        if(j==m){
                            //break;
                        }else{
                            MindBeatContent mm = mindBeatContents.get(m);
                            mindBeatContents.set(j,mm);
                            mindBeatContents.set(m,mindBeatContent);
                        }
                        m++;
                    }
                }
            }
        }

        JSONArray urlList = new JSONArray();

        //先按a标签顺序提取图片列表
        for (int i = 0, n = mindBeatContents.size(); i < n; i++) {
            MindBeatContent mindBeatContent = mindBeatContents.get(i);
            String imgs = mindBeatContent.getImgs();
            //{"img87572":[{"url":"http://62.jpg","time":"2000","hover":false}
            // ,{"url":"http://file417.jpg","time":"2000","hover":false}
            // ,{"url":"http:00/44.jpg","time":"2000","hover":false}]}
            if (StringUtils.isBlank(imgs)) {
                continue;
            }
            JSONObject jsonObject = null;
            try {
                jsonObject = JSON.parseObject(imgs);
            } catch (Exception e) {
                continue;
            }

            String content = mindBeatContent.getContent();

            org.jsoup.nodes.Document parse = Jsoup.parse(content);

            if (parse == null) {
                continue;
            }

            Elements a = parse.getElementsByTag("a");
            for (Element b : a) {
                String imgId = b.attr("id");
                if (StringUtils.isBlank(imgId)) {
                    continue;
                }
                JSONArray jsonArray = jsonObject.getJSONArray(imgId);
                if (jsonArray != null) {
                    String text = b.text();
                    for(int j=0,m=jsonArray.size();j<m;j++){
                        JSONObject jsonObject1 = jsonArray.getJSONObject(j);
                        jsonObject1.put("aText", text);
                    }
                    urlList.addAll(jsonArray);
                }
            }

        }
        //创建临时目录
        String tmpDirPath = FileUtils.getTempDirectory() + File.separator + "ccvi-" + pro_id + File.separator;
        File tmp = new File(tmpDirPath);
        //如果有则删除重新创建
        if (tmp.exists()) {
            FileUtil.del(tmpDirPath);
        }
        FileUtil.mkdir(tmpDirPath);
        File inputTxt = new File(tmpDirPath + "input.txt");
        int imgNum = 0;
        long srtStartTime = 0L;
        int srtId = 1;
        //字幕数组
        ArrayList<Srt> srtList = new ArrayList<>();
        for (int i = 0, n = urlList.size(); i < n; i++) {
            JSONObject jsonObject = urlList.getJSONObject(i);
            //数据示例 {"url":"http://file.sxycy.cn:8888/group1/M00/00/07/wKgBCmCI1F6Aa84rAAGDUrYINh4162.jpg","time":"2000","hover":false}
            //图片地址
            String url = jsonObject.getString("url");
            //字幕
            String des = jsonObject.getString("des") == null?jsonObject.getString("aText"): jsonObject.getString("des") ;
            //图片播放时间
            Integer time = jsonObject.getInteger("time");
            Double timeD = time == null ? 2000d / 1000 : time.doubleValue() / 1000;
            File image = new File(tmpDirPath + "image" + i + url.substring(url.lastIndexOf(".")));
            try {
                //网络下载图片,失败则跳过
                FileUtils.copyURLToFile(new URL(url), image);
            } catch (IOException e) {
                continue;
            }
            //如果图片下载成功,则添加文本
            if (image.exists()) {
                try {
                    if (imgNum == 0) {
                        if (!inputTxt.exists()) {
                            inputTxt.createNewFile();
                        }
                        FileUtils.write(inputTxt, "file " + replacePath(image.getAbsolutePath()) + "\n", false);
                        FileUtils.write(inputTxt, "duration 1\n", true);
                        srtList.add(new Srt(srtId++ , srtStartTime, srtStartTime = (srtStartTime + 1000), des));
                    }

                    FileUtils.write(inputTxt, "file " + replacePath(image.getAbsolutePath()) + "\n", true);
                    FileUtils.write(inputTxt, "duration " + timeD + "\n", true);
                    srtList.add(new Srt(srtId++ , srtStartTime, srtStartTime = (srtStartTime + (time)), des));

                    if (i == n - 1) {
                        FileUtils.write(inputTxt, "file " + replacePath(image.getAbsolutePath()) + "\n", true);
                        FileUtils.write(inputTxt, "duration 1\n", true);
                        srtList.add(new Srt(srtId++ , srtStartTime, srtStartTime = (srtStartTime + 1000), des));
                    }
                    imgNum++;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        if (imgNum > 0) {
            File srtFile = SrtUtil.createSrtFile(srtList, tmpDirPath);
            //组装cmd命令
            File outFile = new File(tmpDirPath + "output.mp4");
            ArrayList<String> cmd = new ArrayList();
            cmd.add("ffmpeg");
            cmd.add("-y");
            cmd.add("-safe");
            cmd.add("0");
            cmd.add("-f");
            cmd.add("concat");
            cmd.add("-i");
            cmd.add(replacePath(inputTxt.getAbsolutePath()));
            cmd.add("-r");
            cmd.add("5");
            cmd.add("-vcodec");
            cmd.add("mpeg4");
            if(srtFile != null){
                logger.debug("字幕存在,加载字幕. path: {} size: {}", srtFile.getAbsolutePath(), srtFile.length());
                cmd.add("-vf");
                cmd.add("subtitles=./" + srtFile.getName());
            }
            cmd.add(replacePath(outFile.getAbsolutePath()));
            //执行命令
            boolean res = execCmd(cmd, tmpDirPath);
            //转码成功进入下载
            if (res && outFile.exists()) {
                downloadFile(outFile, null, response, tmpDirPath);
            }
        }

        //ffmpeg -y -safe 0 -f concat -i input.txt -r 5 -vcodec mpeg4 output.mp4
        return "success";
    }

    //修正ffmpeg对windows路径的认识
    private String replacePath(String path) {
        return path.replaceAll("\\\\", "\\\\\\\\");
    }


    /**
     * //执行命令行
     *
     * @param comm4Map
     * @return
     */
    public boolean execCmd(List<String> comm4Map, String workDir) {
        if (comm4Map == null || comm4Map.isEmpty()) {
            return false;
        }
        boolean debug = logger.isDebugEnabled();
        // 执行命令行
        Process process = null;
        BufferedReader br = null;
        try {
            ProcessBuilder pb = new ProcessBuilder(comm4Map);
            //正常信息和错误信息合并输出
            pb.redirectErrorStream(true);
            if(workDir != null){
                pb.directory(new File(workDir));
            }
            process = pb.start();

            if (logger.isDebugEnabled()) {
                logger.debug("开始执行: {}", comm4Map);
            }

            br = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = null;
            while ((line = br.readLine()) != null) {
                if (debug) {
                    logger.debug(line);
                }
            }
            //等待命令子线程执行完成
            int exitValue = process.waitFor();
            //完成后执行回调
//            if (exitValue == 0 && callback != null) {
//                callback.complete(resultInfo);
//            }
            //销毁子线程
            process.destroy();
            process = null;

            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
        return false;
    }


    /**
     * 查询项目所有节点文本,并导出word
     * <p>
     * 利用html css指定样式,再使用asword生成word
     *
     * @param pro_id
     * @param response
     * @return
     */
    public String exportB(Integer pro_id, HttpServletResponse response) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("pro_id", pro_id);
        List<MindBeatContent> mindBeatContents = beatContentService.list(queryWrapper);
        queryWrapper.orderByAsc("x");

        List<MindBeat> beats = beatService.list(queryWrapper);
        List<MindDivide> divides = divideService.list(queryWrapper);

        List list = new ArrayList();
        list.addAll(beats);
        list.addAll(divides);

        //按x排序
        list.sort((o1, o2) -> {
            Object x1 = ReflectionUtils.getValueByFieldName(o1, "x");
            Object x2 = ReflectionUtils.getValueByFieldName(o2, "x");
            if (x1 != null && x2 != null) {
                return Double.compare((Double) x1, (Double) x2);
            }
            return 0;
        });

        org.jsoup.nodes.Document parse = Jsoup.parse("<html><head><title></title><style>body{font-family:'宋体','Microsoft YaHei';}h1,h2,h3,h4{text-align:center;}</style></head><body></body></html");

        Element body = parse.getElementsByTag("body").get(0);


        StringBuffer sb = new StringBuffer();
        for (int i = 0, n = list.size(); i < n; i++) {
            Object o = list.get(i);
            if (o instanceof MindBeat) {
                MindBeat m = (MindBeat) o;
                sb.append("<h5 class='beat-title'>");
                sb.append(m.getTitle());
                sb.append("</h5>");
                MindBeatContent mb = null;
                for (int j = 0, k = mindBeatContents.size(), bid = m.getBeat_id(); j < k; j++) {
                    if (bid == mindBeatContents.get(j).getBeat_id()) {
                        mb = mindBeatContents.get(j);
                        mindBeatContents.remove(j);
                        break;
                    }
                }
                if (mb != null) {
                    sb.append("<div class='beat-content'>");
                    sb.append(mb.getContent());
                    sb.append("</div>");
                }
            } else if (o instanceof MindDivide) {
                MindDivide m = (MindDivide) o;
                if (m.getType().equals(1)) {
                    sb.append("<h2 class='divide1'>");
                    sb.append(m.getTitle());
                    sb.append("</h2>");
                } else if (m.getType().equals(2)) {
                    sb.append("<h3 class='divide2'>");
                    sb.append(m.getTitle());
                    sb.append("</h3>");
                } else if (m.getType().equals(3)) {
                    sb.append("<h4 class='divide3'>");
                    sb.append(m.getTitle());
                    sb.append("</h4>");
                } else if (m.getType().equals(4)) {
                    sb.append("<h5 class='divide4'>");
                    sb.append(m.getTitle());
                    sb.append("</h5>");
                } else {
                    sb.append("<h6 class='divide5'>");
                    sb.append(m.getTitle());
                    sb.append("</h6>");
                }
            }
        }

        body.append(sb.toString());

        //生成html文件,暂时不需要
//        File tempFIle = FileUtil.newFile(FileUtil.getTmpDirPath() + "\\ccvi\\" + UUIDUtils.getGUID() + ".html");

//        FileUtil.appendString(parse.html(), tempFIle, "gbk");

        try {
            // 验证License
            if (!LicenseVerify.getWordsLicense()) {
                logger.error("HTML To Word Failed! Permission validation failed!");
            }

            //asword html编码生成word
            com.aspose.words.Document doc = new com.aspose.words.Document();
            DocumentBuilder build = new DocumentBuilder(doc);
            Font font = build.getFont();
            font.setName("宋体");
            build.insertHtml(parse.html());
            //设置生成文件路径及随机文件名
            String wordFile = FileUtil.getTmpDirPath() + File.separator + "ccvi" + File.separator + UUIDUtils.getGUID() + ".docx";
            //设置多个字体目录
            if (OSInfo.isLinux()) {
                FontSettings.setFontsFolders(new String[]{"/usr/share/Fonts"}, true);
            } else {
                FontSettings.setFontsFolders(new String[]{"C:" + File.separator + "Windows" + File.separator + "Fonts"}, true);
            }

//            com.aspose.words.DocSaveOptions saveOptions = new com.aspose.words.DocSaveOptions();

            //生产word文档
            doc.save(wordFile, SaveFormat.DOCX);
//            FileUtil.del(tempFIle);
            //转入下载
            downloadFile(new File(wordFile), null, response, null);
            return "";
        } catch (Exception e) {
            //e.printStackTrace();
            return e.getMessage();
        }
    }

    /**
     * @param file
     * @param fileName 下载文件名称,可为空. 为空则设置file的文件名
     * @param response
     */
    private void downloadFile(File file, String fileName, HttpServletResponse response, String delPath) {
        try {
            //转换后生成的文件
            InputStream in = new FileInputStream(file);
            OutputStream out = response.getOutputStream();
//            response.setHeader("Content-Type", Files.probeContentType(Paths.get(file.getName())));
//            response.setContentType("application/msword");
//            response.setContentType("application/force-download");// 设置强制下载不打开
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName == null ? file.getName() : fileName, "UTF-8"));
            int len = 0;
            byte[] bytes = new byte[1024 * 100];
            while ((len = in.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            }
//            out.flush();
            out.close();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            logger.debug("下载完成,正在清理临时目录: {}", file.getAbsolutePath());
            FileUtil.del(file);
            if(delPath != null){
                FileUtil.del(delPath);
            }
        }
    }


}
